﻿using System;
using System.Collections.Generic;
using UnityEngine;
namespace IFramework
{
    public class Pool : IPool
    {
        private Type poolType;
        public Type PoolType
        {
            get
            {
                return poolType;
            }
        }

        private int idelCapcity;
        public int IdelCapcity
        {
            get
            {
                return idelCapcity;
            }
        }

        public Pool(Type poolType, int idelCapcity)
        {
            this.poolType = poolType;
            this.idelCapcity = idelCapcity;
        } 
        private List<IPoolObj> WorkingPool = new List<IPoolObj>();
        private Queue<IPoolObj> IdelPool = new Queue<IPoolObj>();
        private Stack<IPoolObj> HelpStack = new Stack<IPoolObj>();
        private IPoolObj GetReuseObj(object objParent, params object[] paras) 
        {
            IPoolObj tempObj = IdelPool.Dequeue();
            lock (WorkingPool)
            {
                WorkingPool.Add(tempObj);
            }
            tempObj.OnReuse(paras);
            return tempObj;
        }
        private IPoolObj InitNewObj(IPoolObj poolObj, object objParent,params object[] paras) 
        {
            lock (WorkingPool)
            {
                WorkingPool.Add(poolObj);
            }
            poolObj.OnCreate(paras);
            return poolObj;
        }
        public T GetInitObj<T>(T prefab, object PoolObjParent, params object[] paras)where T:UnityEngine.Object,IPoolObj
        {
            lock (IdelPool)
            {
                if (IdelPool.Count != 0)
                    GetReuseObj(PoolObjParent, paras);
            }
            return (T)InitNewObj(GameObject.Instantiate(prefab), PoolObjParent, paras);
        }
        public T GetPoolObj<T>(object PoolObjParent, params object[] paras) where T : IPoolObj, new()
        {
            lock (IdelPool)
            {
                if (IdelPool.Count != 0)
                    GetReuseObj(PoolObjParent, paras);
            }
            return (T)InitNewObj(new T(), PoolObjParent, paras);
        }
        public void Restore(IPoolObj poolObj,params object[] paras)
        {
            lock (WorkingPool)
            {
                if (WorkingPool.Contains(poolObj))
                    WorkingPool.Remove(poolObj);
            }
            lock(IdelPool)
            {
                IdelPool.Enqueue(poolObj);
            }
            poolObj.OnRestore(paras);
            if (IdelPool.Count>idelCapcity)
            {
                AutoClean(true, paras);
            }
        }
        public void Restore(IPoolObj[] poolObjs, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Restore(poolObjs[i], paras);
            }
        }
        public void RestoreAll(params object[] paras)
        {
            for (int i = 0; i < WorkingPool.Count; i++)
            {
                Restore(WorkingPool[i], paras);
            }
        }


        public void AutoClean(bool isHalf, params object[] paras)
        {
            lock (IdelPool)
            {
                if (isHalf)
                {
                    while (idelCapcity > IdelPool.Count * 2)
                    {
                        IPoolObj tempObj = IdelPool.Dequeue();
                        PoolManager.Instance.OnClean(tempObj);
                        tempObj.OnClean(paras);
                    }
                }
                else
                {
                    while (idelCapcity > 1)
                    {
                        IPoolObj tempObj = IdelPool.Dequeue();
                        PoolManager.Instance.OnClean(tempObj);
                        tempObj.OnClean(paras);
                    }
                }
            }
        }
        public void Clean(params object[] paras)
        {
            AutoClean(false,paras);
        }

        public void Clean(IPoolObj poolObj, params object[] paras)
        {
            if (!IdelPool.Contains(poolObj)) return;
            lock (IdelPool)
            {
                lock (HelpStack)
                {
                    while (IdelPool.Peek() != poolObj)
                    {
                        HelpStack.Push(IdelPool.Dequeue());
                    }
                    PoolManager.Instance.OnClean(IdelPool.Dequeue());
                    while (HelpStack.Count > 0)
                    {
                        IdelPool.Enqueue(HelpStack.Pop());
                    }
                }
            }
        }

        public void Clean(IPoolObj[] poolObjs, params object[] paras)
        {
            for (int i = 0; i < poolObjs.Length; i++)
            {
                Clean(poolObjs[i], paras);
            }
        }
    }
}
        
